package com.masnegocio.pac.servicio;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Signature;
import java.security.SignatureException;

import org.apache.log4j.Logger;

import com.masnegocio.pac.comun.PacConstantes;
import com.masnegocio.pac.comun.PacException;
import com.masnegocio.pac.dto.DatosDto;
import com.masnegocio.pac.dto.PacDto;
import com.masnegocio.pac.dto.PacEstatusDto;
import com.masnegocio.pac.servicio.PacServicio;
import com.masnegocio.pac.util.xml.ManagerXml;

public class PacServicio {
	private static final Logger logger = Logger.getLogger(PacServicio.class);
	
	/**
	 * 
	 * @param pacDto contiene el xml a validar
	 * @return regresa el estatus de exito
	 * @throws PacException en caso de no cumplirse alguna validacion el proceso genera una PacException
	 */
	public PacEstatusDto validacion(PacDto pacDto) throws PacException{
		if(logger.isDebugEnabled()){
			logger.debug("SE EMPIEZA A REALIZAR LA VALIDACION PARA EL CLIENT ID: "+pacDto.getClientId());
		}
		DatosDto datosDto = ManagerXml.obtenerDatosXSLT( pacDto.getCfdiBtyteArray());
		if(logger.isInfoEnabled()){
			logger.info("SE EMPIEZA LA VALIDACION PARA EL CLIENT ID: "+pacDto.getClientId()+" CON RFC:"+datosDto.getRfcEmisor()+" CON FOLIO: "+datosDto.getFolio()+" Y VERSION: "+datosDto.getVersion());
		}
		if(logger.isDebugEnabled()){
			logger.debug("LA VALIDACION ES PARA LA VERSION: "+datosDto.getVersion());
		}
		if(ManagerXml.validateXml(datosDto.getVersion(),pacDto.getCfdiBtyteArray())){
			if(logger.isDebugEnabled()){
				logger.debug("LA VALIDACION DEL XML CONTRA EL XSD VERSION: "+datosDto.getVersion()+" ES CORRECTA");
			}
			datosDto.setCadenaOriginal(ManagerXml.generarCadenaOriginal(datosDto.getVersion(),pacDto.getCfdiBtyteArray()));
			if(logger.isDebugEnabled()){
				logger.debug("SE GENERO LA CADENA ORIGINAL CON EL XSLT VERSION: "+datosDto.getVersion());
			}
			if(validaLlaves(datosDto)){
				if(logger.isDebugEnabled()){
					logger.debug("LA VALIDACION DE LOS CAMPOS SENSIBLES, CADENA ORIGINAL, SELLO Y CERTIFICADO ES EXITOSSA");
				}
				if(datosDto.validaFecha()&&datosDto.validaRFC()){
					if(logger.isDebugEnabled()){
						logger.debug("EL CERTIFICADO ES VALIDO, PARA LA FECHA DE EMISION Y EL RFC DE QUIEN LO EMITIO");
						logger.debug("SE DA POR CONCLUIDO EL PROCESO DE VALIDACION");
					}
					PacEstatusDto pacEstatusDto = new PacEstatusDto();
					pacEstatusDto.setEstatus(PacConstantes.CODIGO_ESTATUS_EXITO);
					pacEstatusDto.setCodigo(PacConstantes.CODIGO_EXITO_OPERACION);
					if(logger.isInfoEnabled()){
						logger.info("SE TERMINA LA VALIDACION PARA EL CLIENT ID: "+pacDto.getClientId()+" CON RFC:"+datosDto.getRfcEmisor()+" CON FOLIO: "+datosDto.getFolio()+" Y VERSION: "+datosDto.getVersion()+"CON ESTATUS: "+pacEstatusDto.getEstatus()+" CODIGO: "+pacEstatusDto.getCodigo()+" DESCRIPCION: "+pacEstatusDto.getDescripcion());
					}
					return pacEstatusDto;
				}else{
					if(logger.isDebugEnabled()){
						logger.debug("LA FECHA O EL RFC NO SON VALIDOS PARA EL CERTIFICADO");
					}
					PacEstatusDto pacEstatusDto = new PacEstatusDto();
					pacEstatusDto.setEstatus(PacConstantes.CODIGO_ESTATUS_FALLO);
					pacEstatusDto.setCodigo(PacConstantes.CODIGO_ERROR_TEMPORALIDAD);
					if(logger.isInfoEnabled()){
						logger.info("SE TERMINA LA VALIDACION PARA EL CLIENT ID: "+pacDto.getClientId()+" CON RFC:"+datosDto.getRfcEmisor()+" CON FOLIO: "+datosDto.getFolio()+" Y VERSION: "+datosDto.getVersion()+"CON ESTATUS: "+pacEstatusDto.getEstatus()+" CODIGO: "+pacEstatusDto.getCodigo()+" DESCRIPCION: "+pacEstatusDto.getDescripcion());
					}
					return pacEstatusDto;
				}
			}else{
				if(logger.isDebugEnabled()){
					logger.debug("LOS DATOS SENSIBLES NO CORRESPONDEN");
				}
				PacEstatusDto pacEstatusDto = new PacEstatusDto();
				pacEstatusDto.setEstatus(PacConstantes.CODIGO_ESTATUS_FALLO);
				pacEstatusDto.setCodigo(PacConstantes.CODIGO_ERROR_DATO_SENSIBLE);
				if(logger.isInfoEnabled()){
					logger.info("SE TERMINA LA VALIDACION PARA EL CLIENT ID: "+pacDto.getClientId()+" CON RFC:"+datosDto.getRfcEmisor()+" CON FOLIO: "+datosDto.getFolio()+" Y VERSION: "+datosDto.getVersion()+" CON ESTATUS: "+pacEstatusDto.getEstatus()+" CODIGO: "+pacEstatusDto.getCodigo()+" DESCRIPCION: "+pacEstatusDto.getDescripcion());
				}
				return pacEstatusDto;
			}
		}else{
			if(logger.isDebugEnabled()){
				logger.debug("EL XML NO CORRESPONDE CON EL XSD DE LA VERSION: "+datosDto.getVersion());
			}
			PacEstatusDto pacEstatusDto = new PacEstatusDto();
			pacEstatusDto.setEstatus(PacConstantes.CODIGO_ESTATUS_FALLO);
			pacEstatusDto.setCodigo(PacConstantes.CODIGO_ERROR_VALIDEZ_XSD);
			if(logger.isInfoEnabled()){
				logger.info("SE TERMINA LA VALIDACION PARA EL CLIENT ID: "+pacDto.getClientId()+" CON RFC:"+datosDto.getRfcEmisor()+" CON FOLIO: "+datosDto.getFolio()+" Y VERSION: "+datosDto.getVersion()+" CON ESTATUS: "+pacEstatusDto.getEstatus()+" CODIGO: "+pacEstatusDto.getCodigo()+" DESCRIPCION: "+pacEstatusDto.getDescripcion());
			}
			return pacEstatusDto;
		}
	}
	
	/**
	 * 
	 * @param datosDto contiene los datos sensibles necesarios para la validacion
	 * @return boolean en casa de que cumpla o no con el certificado
	 * @throws PacException si susede alguna exception dentro del proceso se genera una exception
	 */
	public boolean validaLlaves(DatosDto datosDto) throws PacException{
		if(logger.isDebugEnabled()){
			logger.debug("SE VAN A VALIDAR LOS DATOS SIMBLES DEL CFDI VERSION: "+datosDto.getVersion());
		}
		Signature sign = null;
		try {
			sign = Signature.getInstance(PacConstantes.CODIGO_TIPO_ALGORITMO);
			if(logger.isDebugEnabled()){
				logger.debug("SE OBTUVO UNA INSTANCIA DEL OBJETO Signature PARA EL ALGORITMO: "+PacConstantes.CODIGO_TIPO_ALGORITMO);
			}
		} catch (NoSuchAlgorithmException e) {
			logger.error("OCURRIO UN ERROR AL MOMENTO DE CREAR EL SIGNATURE LA EXCEPTION ES: " + e);
			throw new PacException(e,PacConstantes.CODIGO_ERROR_CREAR_SIGNATURE);
		}
		try {
			sign.initVerify(datosDto.getCertificado());
			if(logger.isDebugEnabled()){
				logger.debug("FUE INICIADO EL OBJETO Signature CON EL CERTIFICADO");
			}
		} catch (InvalidKeyException e) {
			logger.error("OCURRIO UN ERROR AL MOMENTO DE INICIALIZAR EL SIGNATURE LA EXCEPTION ES: " + e);
			throw new PacException(e,PacConstantes.CODIGO_ERROR_INIT_SIGNATURE);
		}
        try {
			sign.update(datosDto.getCadenaOriginalDecode());
			if(logger.isDebugEnabled()){
				logger.debug("FUE ACTUALIZADO EL OBJETO Signature CON LA CADENA ORIGINAL");
			}
		} catch (SignatureException e) {
			logger.error("OCURRIO UN ERROR AL MOMENTO DE ACTUALIZAR EL MOMENTO SIGNATURE: " + e);
			throw new PacException(e,PacConstantes.CODIGO_ERROR_UPDATE_SIGNATURE);
		}
        try {
        	if(logger.isDebugEnabled()){
    			logger.debug("SE PROCEDE A VERIFICAR LOS DATOS");
    		}
			return sign.verify(datosDto.getSelloDecode());
		} catch (SignatureException e) {
			logger.error("OCURRIO UN ERROR AL MOMENTO DE VERIFICAR LOS DATOS SENSIBLES LA EXCEPTION ES: " + e);
			throw new PacException(e,PacConstantes.CODIGO_ERROR_VERIFY_SIGNATURE);
		}
	}
}
